#include <stdlib.h>
#include <stdbool.h>
#include <stdio.h>   /* For debug */
#include <assert.h>

#include "linked_list.h"

struct list {
	struct node * head; /* the 'front' */
	struct node * tail;
};

struct node {
	struct node * next;
	list_data_t data;
};

struct list * list_init()
{
	struct list * l = NULL;
	if (!(l = malloc(sizeof(*l))))
		goto err;

	l->head = NULL;
	l->tail = NULL;
	assert((!l->head) || (l->tail));
err:
	return l;
}

void list_destroy(struct list * l)
{
	if (!l)
		return;
	while(l->head)
		list_pop_front(l, NULL);
	free(l);
	l = NULL;
}

int list_push_back(struct list * l, list_data_t data)
{
	int ret = -1;
	struct node * node = NULL;
	if (!(node = malloc(sizeof(*node))))
		goto err;
	node->next = NULL;
	node->data = data;
	if (l->tail) {
		l->tail->next = node;
		l->tail = node;
	} else {
		l->head = node;
		l->tail = node;
	}
	assert((!l->head) || (l->tail));
	ret = 0;
err:
	return ret;
}

int list_pop_front(struct list * l, list_data_t* out_data)
{
	int ret = -1;
	struct node * node;
	if ((!l) || (!l->head))
		goto err;
	
	node = l->head;
	l->head = node->next;
	if (!l->head)
		l->tail = NULL;
	if (NULL != out_data) {
		*out_data = node->data;
	}
	free(node);
	ret = 0;
	assert((!l->head) || (l->tail));
err:
	return ret;
}


int list_get_front(struct list * l, list_data_t* out_data)
{
	assert ((NULL != l) && (NULL != out_data));
	if (NULL == l->head) {
		return -1;
	}
	assert((!l->head) || (l->tail));
	*out_data = l->head->data;
	return 0;
}


int list_remove_first_occurrence(struct list * l, list_data_t data)
{
	int ret = -1;
	if ((!l) || (!l->head))
		goto err;

	struct node * node;
	struct node * prev; /* The unit previouse to 'node' */

	node = l->head;
	prev = NULL;
	while ((NULL != node) && (node->data != data)) {
		prev = node;
		node = node->next;
	}
	if (NULL == node) {
		goto err;
	}
	assert (node->data == data);
	/* 'node' points to the wanted node. */
	if (NULL != prev) {
		assert (node != l->head);
		assert (prev->next == node);

		prev->next = node->next;
	} else {
		assert (node == l->head);
	}

	if (node == l->head) {
		l->head = node->next;
	}
	if (node == l->tail) {
		l->tail = prev;
	}
	assert((!l->head) || (l->tail));
	free(node);
	ret = 0;
err:
	return ret;
}


bool list_is_empty(struct list * l) {
	return (NULL == l->head);
}

void list_debug_print(struct list * l)
{
	struct node * n;
	n = l->head;
	while (NULL != n) {
		printf("%p ", n->data);
		n = n->next;
	}
}

